/*****************************************************************************

                 ///////,   .////    .///' /////,
                ///' ./// ///'///  ///,     '///
               ///////'  ///,///   '/// //;';//,
             ,///' ////,'/////',/////'  /////'/;,

    Copyright 2010 Marcus Jansson <mjansson256@yahoo.se>

    This file is part of ROSA - Realtime Operating System for AVR32.

    ROSA is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ROSA is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ROSA.  If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
/* Tab size: 4 */

#include <stdlib.h>
#include <stdint.h>
#include <malloc.h>
#include <string.h>

//Kernel includes
#include "kernel/rosa_task.h"
#include "kernel/rosa_ext.h"
#include "kernel/rosa_tim.h"
#include "kernel/rosa_scheduler.h"
#include "kernel/rosa_queues.h"
#include "kernel/rosa_systick.h"

#include "API/rosa.h"

//Driver includes
#include "drivers/button.h"
#include "drivers/led.h"
#include "drivers/pot.h"
#include "drivers/usart.h"

/// Define the initial value of the status register
#define ROSA_INITIALSR 0x1c0000

// Idle task: task that will free up all tasks that are deleted
#define IDLE_STACK_SIZE 0x100

void IdleTaskFunc(void)
{
	while(1)
	{
		if(deleteQueue != NULL)
		{
			stopSysTick();
			freeTask(rosa_removeFromDelete());
			startSysTick();
		}
		else
		{
			ROSA_sleep();
		}
	}
}

/// Terminates the currently executing task
void ROSA_taskTerminate(void)
{
	tcb *task = getCurrentTask();
	task->state = Terminated;
	scheduler();
}

/// Initializes ROSA. Should be called before calling driver functions
void ROSA_init(void)
{
	//Do initialization of I/O drivers
	ledInit();									//LEDs
	buttonInit();								//Buttons
	joystickInit();								//Joystick
	potInit();									//Potentiometer
	usartInit(USART, &usart_options, FOSC0);	//Serial communication

	//Start with no EXECTASK.
	
	ROSA_taskCreate("idle", IdleTaskFunc, 0, IDLE_STACK_SIZE);
	
	//Initialize the timer to 1 ms period.
	//...
	interruptInit();
	if((TICKSIZE < 1) || (TICKSIZE > 100))
		usartWriteLine(USART, "Bad tick size!");
		
	timerInit(TICKSIZE);
	timerStart();
	//...
}

static tcb *lastCreatedTask;
/// Task creation function
int ROSA_taskCreate(char *taskName, void *taskFunction, unsigned int priority, unsigned int stackSize)
{
	if(systickIsRunning())
	{
		stopSysTick();							// Protect the delete queue from corruption
		while(deleteQueue != NULL)
		{
			freeTask(rosa_removeFromDelete());
		}
		startSysTick();
	}

	// loop over all tasks already created and check if they have the name taskName
	tcb *nameIterator;
	nameIterator = readyQueueHead;
	
	while (nameIterator != NULL)
	{
		if (strncmp(taskName, nameIterator->id, NAMESIZE) == 0)
		{
			return -1;
		}
		nameIterator = nameIterator->nexttcb;
	}
	
	tcb *tcpTask = allocateTask(stackSize);	
	if (tcpTask == NULL )
	{
		return -2;
 	}
	lastCreatedTask = tcpTask;
	
	tcpTask->saveusp = tcpTask->dataarea + tcpTask->datasize - STACK_MARGIN;	// Ensure that there's enough space for a supervisor call stack frame (with margin)
	tcpTask->saveusp = (unsigned char *)((intptr_t)tcpTask->saveusp & (intptr_t)(~0x3)); // Ensure that the stack pointer is word-aligned.
	
	//Set the task priority
	tcpTask->priority = priority;
	tcpTask->dynamicPriority = priority;
	
	//Initialize the tcb with the correct values
	for (int i = 0; i < NAMESIZE; i++) {
		//Copy the id/name
		tcpTask->id[i] = taskName[i];
	}
	
	//Don't link this TCB anywhere yet.
	tcpTask->nexttcb = NULL;

	//Set the task function start and return address.
	tcpTask->staddr = taskFunction;
	tcpTask->retaddr = (int)taskFunction;

	//Set up the stack.
	tcpTask->datasize = stackSize;

	//Set the initial SR.
	
	tcpTask->savesr = ROSA_INITIALSR;
	tcpTask->state = Ready;
	
	tcpTask->lockedSemaphoresListHead = NULL;
	tcpTask->nextInBlockList = NULL;
	tcpTask->semaphoreThatWereWaitingFor = NULL;
	tcpTask->semStatus = 0;
	
	//Initialize context.
	contextInit(tcpTask);

	rosa_addToReadyQueue(tcpTask);
	scheduler();						// Call scheduler. We might have to switch task if we created a high-priority task
	return 0;
}